Archetype.useArchetypeClass = true;
Archetype.classOverride = true;

var $break = { };
var $identity = function(x) {return x;};

Class = {};

Object.extend = jQuery.extend;
Object.clone = function(object) {
    return Object.extend({}, object);
};
Object.isArray = function(object) {
    return object != null && typeof object == "object" &&
    'splice' in object && 'join' in object;
};

Object.extend(String.prototype, {
    strip: function() {
        return this.replace(/^\s+/, '').replace(/\s+$/, '');
    },

    toQueryParams: function(separator) {
        var match = this.strip().match(/([^?#]*)(#.*)?$/);
        if (!match) return { };

        return match[1].split(separator || '&').inject({ }, function(hash, pair) {
            if ((pair = pair.split('='))[0]) {
                var key = decodeURIComponent(pair.shift());
                var value = pair.length > 1 ? pair.join('=') : pair[0];
                if (value != undefined) value = decodeURIComponent(value);

                if (key in hash) {
                    if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
                    hash[key].push(value);
                }
                else hash[key] = value;
            }
            return hash;
        });
    }
});

Object.extend(Function.prototype, {
    bind: function() {
        if (arguments.length < 2 && typeof arguments[0] == "undefined") return this;
        var __method = this, args = $A(arguments), object = args.shift();
        return function() {
            arguments.callee.toSource = "Bound method source: " + __method.toSource;
            arguments.callee.name = __method.name;
            return __method.apply(object, args.concat($A(arguments)));
        };
    }
});

var Enumerable = {
    each: function(iterator, context) {
        var index = 0;
        try {
            this._each(function(value) {
                iterator.call(context, value, index++);
            });
        } catch (e) {
            if (e != $break) throw e;
        }
        return this;
    },

    eachSlice: function(number, iterator, context) {
        var index = -number, slices = [], array = this.toArray();
        if (number < 1) return array;
        while ((index += number) < array.length)
            slices.push(array.slice(index, index+number));
        return slices.collect(iterator, context);
    },

    all: function(iterator, context) {
        iterator = iterator || $identity;
        var result = true;
        this.each(function(value, index) {
            result = result && !!iterator.call(context, value, index);
            if (!result) throw $break;
        });
        return result;
    },

    any: function(iterator, context) {
        iterator = iterator || $identity;
        var result = false;
        this.each(function(value, index) {
            if (result = !!iterator.call(context, value, index))
                throw $break;
        });
        return result;
    },

    collect: function(iterator, context) {
        iterator = iterator || $identity;
        var results = [];
        this.each(function(value, index) {
            results.push(iterator.call(context, value, index));
        });
        return results;
    },

    detect: function(iterator, context) {
        var result;
        this.each(function(value, index) {
            if (iterator.call(context, value, index)) {
                result = value;
                throw $break;
            }
        });
        return result;
    },

    findAll: function(iterator, context) {
        var results = [];
        this.each(function(value, index) {
            if (iterator.call(context, value, index))
                results.push(value);
        });
        return results;
    },

    grep: function(filter, iterator, context) {
        iterator = iterator || $identity;
        var results = [];

        if (Object.isString(filter))
            filter = new RegExp(filter);

        this.each(function(value, index) {
            if (filter.match(value))
                results.push(iterator.call(context, value, index));
        });
        return results;
    },

    include: function(object) {
        if (typeof this.indexOf == "function")
            if (this.indexOf(object) != -1) return true;

        var found = false;
        this.each(function(value) {
            if (value == object) {
                found = true;
                throw $break;
            }
        });
        return found;
    },

    inGroupsOf: function(number, fillWith) {
        fillWith = Object.isUndefined(fillWith) ? null : fillWith;
        return this.eachSlice(number, function(slice) {
            while(slice.length < number) slice.push(fillWith);
            return slice;
        });
    },

    inject: function(memo, iterator, context) {
        this.each(function(value, index) {
            memo = iterator.call(context, memo, value, index);
        });
        return memo;
    },

    invoke: function(method) {
        var args = $A(arguments).slice(1);
        return this.map(function(value) {
            return value[method].apply(value, args);
        });
    },

    max: function(iterator, context) {
        iterator = iterator || $identity;
        var result;
        this.each(function(value, index) {
            value = iterator.call(context, value, index);
            if (result == null || value >= result)
                result = value;
        });
        return result;
    },

    min: function(iterator, context) {
        iterator = iterator || $identity;
        var result;
        this.each(function(value, index) {
            value = iterator.call(context, value, index);
            if (result == null || value < result)
                result = value;
        });
        return result;
    },

    partition: function(iterator, context) {
        iterator = iterator || $identity;
        var trues = [], falses = [];
        this.each(function(value, index) {
            (iterator.call(context, value, index) ?
                trues : falses).push(value);
        });
        return [trues, falses];
    },

    pluck: function(property) {
        var results = [];
        this.each(function(value) {
            results.push(value[property]);
        });
        return results;
    },

    reject: function(iterator, context) {
        var results = [];
        this.each(function(value, index) {
            if (!iterator.call(context, value, index))
                results.push(value);
        });
        return results;
    },

    sortBy: function(iterator, context) {
        return this.map(function(value, index) {
            return {
                value: value,
                criteria: iterator.call(context, value, index)
            };
        }).sort(function(left, right) {
            var a = left.criteria, b = right.criteria;
            return a < b ? -1 : a > b ? 1 : 0;
        }).pluck('value');
    },

    toArray: function() {
        return this.map();
    },

    zip: function() {
        var iterator = $identity, args = $A(arguments);
        if (typeof args.last() == "function")
            iterator = args.pop();

        var collections = [this].concat(args).map($A);
        return this.map(function(value, index) {
            return iterator(collections.pluck(index));
        });
    },

    size: function() {
        return this.toArray().length;
    },

    inspect: function() {
        return '#<Enumerable:' + this.toArray().inspect() + '>';
    }
};

Object.extend(Enumerable, {
    map:     Enumerable.collect,
    find:    Enumerable.detect,
    select:  Enumerable.findAll,
    filter:  Enumerable.findAll,
    member:  Enumerable.include,
    entries: Enumerable.toArray,
    every:   Enumerable.all,
    some:    Enumerable.any
});
Object.extend(Array.prototype, Enumerable);


Object.extend(Array.prototype, {
    _each: function(iterator) {
        for (var i = 0, length = this.length; i < length; i++)
            iterator(this[i]);
    },

    clear: function() {
        this.length = 0;
        return this;
    },

    first: function() {
        return this[0];
    },

    last: function() {
        return this[this.length - 1];
    },

    compact: function() {
        return this.select(function(value) {
            return value != null;
        });
    },

    flatten: function() {
        return this.inject([], function(array, value) {
            return array.concat(Object.isArray(value) ?
                value.flatten() : [value]);
        });
    },

    without: function() {
        var values = $A(arguments);
        return this.select(function(value) {
            return !values.include(value);
        });
    },

    reverse: function(inline) {
        return (inline !== false ? this : this.toArray())._reverse();
    },

    reduce: function() {
        return this.length > 1 ? this : this[0];
    },

    uniq: function(sorted) {
        return this.inject([], function(array, value, index) {
            if (0 == index || (sorted ? array.last() != value : !array.include(value)))
                array.push(value);
            return array;
        });
    },

    intersect: function(array) {
        return this.uniq().findAll(function(item) {
            return array.detect(function(value) {
                return item === value
            });
        });
    },

    clone: function() {
        return [].concat(this);
    },

    size: function() {
        return this.length;
    },

    inspect: function() {
        return '[' + this.map(Object.inspect).join(', ') + ']';
    }
});

// use native browser JS 1.6 implementation if available
if (typeof Array.prototype.forEach=="function")
    Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
    i || (i = 0);
    var length = this.length;
    if (i < 0) i = length + i;
    for (; i < length; i++)
        if (this[i] === item) return i;
    return -1;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
    var n = this.slice(0, i).reverse().indexOf(item);
    return (n < 0) ? n : i - n - 1;
};

Hash = function() {
    this.initialize.apply(this, arguments);
};
Hash.prototype = {
    initialize: function(object) {
        this._object = object instanceof Hash ? object.toObject() : Object.clone(object);
    },
    each: function(iterator) {
        for (var key in this._object) {
            var value = this._object[key], pair = [key, value];
            pair.key = key;
            pair.value = value;
            iterator(pair);
        }
    },
    pluck: function(property) {
        var results = [];
        this.each(function(value) {
            results.push(value[property]);
        });
        return results;
    },
    set: function(key, value) {
        return this._object[key] = value;
    },
    get: function(key) {
        return this._object[key];
    },
    inject: function(memo, iterator, context) {
        iterator = iterator.bind(context);
        this.each(function(value, index) {
            memo = iterator(memo, value, index);
        });
        return memo;
    },
    update: function(object) {
        return new Hash(object).inject(this, function(result, pair) {
            result.set(pair.key, pair.value);
            return result;
        });
    },
    toObject: function() {
        return Object.clone(this._object);
    },
    merge: function(object) {
        return this.clone().update(object);
    },
    keys: function() {
        return this.pluck('key');
    },
    values: function() {
        return this.pluck('value');
    },
    clone: function() {
        return new Hash(this);
    }
}

function $E(element) {
    if(element) {
        return jQuery(element);
    } else {
        return null;
    }
}

function $A(iterable) {
    if (!iterable) iterable = [];
    if (iterable.each && iterable.concat) return iterable;
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
}

function $H(object) {
    return new Hash(object);
}

jQuery.fn.extend({
    setStyle: function(styles) {
        $H(styles).keys().each(function(key) {
            this.css(key, styles[key]);
        }.bind(this));
    },
    update: function(content) {
        this.html(content);
    },
    observe: function(type, callback) {
        return this.bind(type, callback);
    },
    stopObserving: function(type, callback) {
        return this.unbind(type, callback);
    },
    insert: function(content) {
    	if(typeof content == "string") {
    		this.append(content);
    	} else {
    		if(typeof content.bottom == "string") {
    			this.append(content.bottom);
    		}
    		if(typeof content.top == "string") {
    			this.prepend(content.top);
    		}
    		if(typeof content.after == "string") {
    			this.after(content.after);
    		}
    		if(typeof content.before == "string") {
    			this.before(content.before);
    		}
    	}
    }
});

$$ = function() {
    return $A(jQuery.apply(this, arguments));
}

jQuery.extend(Archetype.engine, {
    fire: function(type, data) {
        return jQuery(document).trigger(type, data);
    },
    observe: function(type, callback) {
        return jQuery(document).bind(type, function() {
            var __method = this, args = $A(arguments), event = args.shift();
            callback.apply(__method, args);
        });
    },
    stopObserving: function(type, callback) {
        return jQuery(document).unbind(type, callback);
    },
    ajax: function(url, asynchronous, success, error) {
        jQuery.ajax({
            url: url,
            async: asynchronous,
            error: error,
            success: success
        });
    }
});